#ifndef _HEADER_H_
#define _HEADER_H_
#include "argpars.h"
#include <cmath>

const char * const FILEERROR_CANNOTOPEN="Error: cannot open the output file.";
const char * const LOGICERROR_INVAILDID="Error: try to get a not exist object by id.";
const char * const LOGICERROR_TIME="Error: Time turned back.";
const char * const LOGICERROR_LINKTAB="Error: Link table counter error.";
const char * const MEMERROR_ALLOC="Error: allocating memory.";
const char * const MEMERROR_FREE="Error: deleting memory.";
const char * const LOGICERROR_TOOMANYEVENT="Error: too many event.";
const char * const LOGICERROR_QUEUETYPEUNKNOWN="Error: Queue type unknown.";
const char * const LOGICERROR_EMPTYQUEUE="Error: try to dequeue an empty queue.";
const char * const LOGICERROR_EMPTYSERVER="Error: try to deserver/getEntity from an empty server.";
const char * const LOGICERROR_ENSERVERNONEMPTYSERVER="Error: try to enserver a non-empty server.";
const char * const MATHERROR_ST="Error: math error.";
const char * const LOGICERROR_NULLQUEUE="Error: operating a NULL queue.";
const char * const LOGICERROR_NULLSERVER="Error: operating a NULL server.";
const char * const LOGICERROR_ENTITYNOTEXIST="Error: try to get a not exist entity by queue index.";
const char * const LOGICERROR_EVENTLISTEMPTY="Error: event list is empty.";
const char * const LOGICERROR_SERVICEDURATION="Error: service duration not set.";
//random-source.cpp

double expoRand(double lambda);
double unifRand(double a, double b);
void setSeed(unsigned seed);

class PoissonProcess {
private:
	double *eTime,lambda;
	unsigned long mElem;
	void doubleSize();
public:
	PoissonProcess(double lam);
	~PoissonProcess();
	unsigned value(double time);
	double getLambda();
	double event(unsigned n); 
};

//entities.cpp

struct linkTab {
	double enqueue,enserver,deserver;
	int queueId,serverId;
	bool balk;
	linkTab *next;
};

class entity {
private:
	unsigned _id;
	linkTab *data;
	entity(linkTab *d,unsigned id);
public:
	entity();
	double &enqueue();
	double &enserver();
	double &deserver();
	int &queueId();
	int &serverId();
	bool &balk();
	unsigned getId();
	friend class PopulationMgr;
};

class PopulationMgr {
private:
	unsigned count;
	linkTab *tab;
public:
	bool report;
	bool file;
	PopulationMgr();
	~PopulationMgr();
	unsigned getCount();
	entity getEntity(unsigned id);
	entity newEntity();
	unsigned balkCount();
};

//queue.cpp
struct Qnode {
	entity *buffer,*queueHead;
	unsigned size;
	unsigned bufferSize;
	unsigned count;
	unsigned id;
	struct {
		unsigned dequeueCounter;
		double timeLength;
		double totalWaitingTime;
		double numInQueueIntegral;
		unsigned balk;
	} st;
	bool *report;
	char * type;
	Qnode *next;
};

class Queue {
private:
	Qnode *data;
	entity* index2Pointer(unsigned i);
	void eventHandler(double time);
	void term(double time);
	entity* dequeue_by_policy();
public:
	Queue();
	unsigned getCount();
	unsigned getSize();
	unsigned getId();
	void enqueue(entity e, double time);
	entity dequeue(double time);
	entity getEntity(unsigned i);	
	friend class QueueMgr;
};

class QueueMgr {
private:
	unsigned count;
	Qnode *head;
public:
	void term(double time);
	bool report;
	QueueMgr();
	~QueueMgr();
	unsigned getCount();
	double avgWaitingTime();
	Queue getQueue(unsigned i);
	Queue newQueue(char * type, unsigned buffer_size);	
};

struct Snode {
	entity *customer;
	bool idle;
	unsigned id;
	double  (*serviceDuration)();
	struct {
		unsigned deserverCounter;
		double totalDequeuedServiceTime;
		double idleTime;
		double totalTime;
	} st;
	bool *report;
	Snode *next;
};

class Server {
private:
	Snode *data;
	void term(double time);
	void eventHandler(double time);
public:
	Server();
	unsigned getId();
	bool isIdle();
	void enserver(entity e, double time);
	void enserver(Queue q, double time);
	void serviceDurationFunc(double (*func)());
	double serviceDuration();
	entity deserver(double time);
	entity getEntity();
	friend class ServerMgr;
};

class ServerMgr {
private:
	Snode *head;
	unsigned count;
public:
	bool report;
	ServerMgr();
	~ServerMgr();
	unsigned getCount();
	unsigned totalServed();
	double avgServiceTime();
	Server getServer(unsigned i);
	Server newServer();
	void term(double time);
};

enum eventType{
	ServiceFinish,
	Arrival
};

struct event {
	eventType type;
	double time;
	Server server;
};

struct eventNode {
	event content;
	eventNode *next;
};

class EventCalendar {
private:
	eventNode *head;
public:
	EventCalendar();
	~EventCalendar();
	void addEvent(event);
	event popNextEvent();
};

class Scenario {
protected:
	PopulationMgr popMgr;
	EventCalendar eventCalendar;
	ServerMgr serverMgr;
	QueueMgr queueMgr;
	double clock;
	virtual void arrivalHandler(entity e);
	virtual void serviceFinishHandler(Server s);
	virtual bool termCondition();
	virtual double arrivalDuration();
	void assign2Queue(entity e,unsigned queueId,double time);
	void assign2Server(entity e,unsigned serverId,double time);
public:
	virtual void init();
	bool report;
	bool file;
	Scenario();	
	~Scenario();	
	void run();
};
//problem.cpp
inline double BPRand(double k, double p, double a) {
	return k/pow(1-unifRand(0,1)*(1-pow(k/p,a)),1/a);
};

extern Scenario &scen1;
extern Scenario &scen2expo;
extern Scenario &scen2const;
extern Scenario &scen2unif;
#endif
